Apple II
Technical Notes
_____________________________________________________________________________
                                                  Developer Technical Support


Apple II Miscellaneous
#2:    Apple II Family Identification Routines 2.1

Revised by:    Matt Deatherage & Keith Rollin                   November 1988
Revised by:    Pete McDonald                                     January 1986

This Technical Note presents a new version of the Apple II Family 
Identification Routine, a sample piece of code which shows how to identify 
various Apple II computers and their memory configurations.
_____________________________________________________________________________


Why Identification Routines?

Although we present the Apple II family identification bytes in Apple II 
Miscellaneous Technical Note #7, many people would prefer a routine they can 
simply plug into their own program and call.  In addition, this routine serves 
as a small piece of sample code, and there is no reason for you to reinvent 
the wheel.

Most of the interesting part of the routine consists of identifying the memory 
configuration of the machine.  On an Apple IIe, the routine moves code into 
the zero page to test for the presence of auxiliary memory.  (A IIe with a 
non-extended 80-column card is a configuration still found in many schools 
throughout the country.)

The actual identification is done by a table-lookup method.


What the Routine Returns

This version (2.1) of the identification routine returns several things:

o    A machine byte, containing one of seven values:
     $00 = Unknown machine
     $01 = Apple ][
     $02 = Apple ][+
     $03 = Apple /// in emulation mode
     $04 = Apple IIe
     $05 = Apple IIc

     In addition, if the high bit of the byte is set, the machine is a 
     IIGS or equivalent.  For all current Apple IIGS computers, the 
     value returned in machine is $84 (high bit set to signify Apple 
     IIGS and $04 because it matches the ID bytes of an enhanced Apple 
     IIe).
o    A ROMlevel byte, indicating the revision of the firmware in the 
     machine.  For example, there are currently five revisions of the 
     IIc, two of the IIe (unenhanced and enhanced), and two versions of 
     the IIGS ROM (there will always be some owners who have not yet 
     upgraded).  These versions are identified starting at $01 for the 
     earliest.  Therefore, the current IIc will return ROMlevel = $04, 
     the current IIGS will return ROMlevel = $02, etc.  The routine 
     will also return correct values for future versions of the IIGS, 
     as a convention has been established for future ROM versions of 
     that machine.
o    A memory byte, containing the amount of memory in the machine.  
     This byte only has four values--0 (undefined), 48, 64, and 128.  
     Extra memory in an Apple IIGS, or extra memory in an Apple IIe or 
     IIc Memory Expansion card, is not included.  Programs must take 
     special considerations to use that memory (if available), beyond 
     those considerations required to use the normal 128K of today's 
     IIe and IIc.
o    If running on an Apple IIGS, three word-length fields are also 
     returned.  These are the contents of the registers as returned by 
     the ID routine in the IIGS ROM, and they indicate several things 
     about the machine.  See Apple II Miscellaneous Technical Note #7 
     for more details.

In addition to these features, most of the addressing done in the routine is 
by label.  If you wish things to be stored in different places, simply 
changing the labels will often do it.


Limitations and Improvements

As sample code, you might have already guessed that this is not the most 
compact, efficient way of identifying these machines.  Some improvements you 
might incorporate if using these routines include:

o    If you are running under ProDOS, you can remove the section that 
     determines how much memory is in the machine (starting at exit, 
     line 127), since the MACHID byte (at $BF98) in ProDOS already 
     contains this information for you.  This change would cut the 
     routine down to less than one page of memory.
o    If you know the ROM is switched in when you call the routine, you 
     can remove the sections which save and restore the language card 
     state.  Be careful in doing so, however, because the memory-
     determination routines switch out the ROM to see if a language 
     card exists.
o    If you need to know if a IIe is a 64K machine with a non-extended 
     80-column card, you may put your own identifying routines in after 
     line 284.  NoAux is only reached if there is an 80-column card but 
     only 64K of memory.


How It Works

The identification routine does the following things:

o    Disables interrupts
o    Saves four bytes from the language card areas so they may be restored 
     later
o    Identifies all machines by a table look-up procedure
o    Calls 16-bit ID routine to distinguish IIGS from other machines of any 
     kind, and returns values in appropriate locations if IIGS ID routine 
     returns any useful information in the registers
o    Identifies memory configuration:
     o    If Apple /// emulation, there is 48K
     o    If Apple ][ or ][+, tests for presence of language card and returns 
          64K if present, otherwise, returns 48K
     o    If Apple IIc or IIGS, returns 128K
     o    If Apple IIe, tries to identify auxiliary memory
          o    If reading auxiliary memory, it must be there
          o    If reading alternate zero page, auxiliary memory is present
          o    If none of this is conclusive:
               o    Exchanges a section of the zero page with a section of code 
                    that switches memory banks.  The code executes in the zero
                    page and does not get switched out when we attempt to
                    switch in the auxiliary RAM.
               o    Jumps to relocated code on page zero:
                    o    Switches in auxiliary memory for reading and writing
                    o    Stores a value at $800 and sees if the same value
                         appears at $C00.  If so, no auxiliary memory is
                         present (the non-extended 80-column card has sparse
                         memory mapping which causes $800 and $C00 to be the
                         same location).
                    o    Changes value at $C00 and sees if the value at $800
                         changes as well.  If so, no auxiliary memory.  If not,
                         then there is 128K available
                    o    Switches main memory back in for reading and writing
               o    Puts the zero page back like we found it
          o    Returns memory configuration found (either 64K or 128K)
o    Restores language card and ROM state from four saved bytes
o    Restores interrupt status
o    Returns to caller

SOURCE   FILE #01 =>ID2.1
 
0000:                1           lst   on
----- NEXT OBJECT FILE NAME IS ID2.1.OBJ                     
2000:        2000    2           org   $2000
2000:                3 ********************************************
2000:                4 *                                          *
2000:                5 *  Apple II Family Identification Program  *
2000:                6 *                                          *
2000:                7 *               Version 2.1                *
2000:                8 *                                          *
2000:                9 *              September, 1988             *
2000:               10 *                                          *
2000:               11 *  Includes support for revisions to IIc   *
2000:               12 *  firmware, and IIgs identification too   *
2000:               13 *                                          *
2000:               14 ********************************************
2000:               15 *
2000:               16 *
2000:               17 *  First, some global equates for the routine:
2000:               18 *
2000:        0001   19 IIplain   equ   $01           ;Apple II
2000:        0002   20 IIplus    equ   $02           ;Apple II+
2000:        0003   21 IIIem     equ   $03           ;Apple /// in emulation mode
2000:        0004   22 IIe       equ   $04           ;Apple IIe
2000:        0005   23 IIc       equ   $05           ;Apple IIc
2000:               24 *
2000:        0001   25 safe      equ   $0001         ;start of code relocated to zp
2000:        0006   26 location  equ   $06           ;zero page location to use
2000:               27 *
2000:        00FB   28 xce       equ   $FB           ;65816 XCE instruction
2000:               29 *
2000:        00AA   30 test1     equ   $AA           ;test byte #1
2000:        0055   31 test2     equ   $55           ;lsr of test1
2000:        0088   32 test3     equ   $88           ;test byte #3
2000:        00EE   33 test4     equ   $EE           ;test byte #4
2000:               34 *
2000:        0400   35 begpage1  equ   $400          ;beginning of text page 1
2000:        0800   36 begpage2  equ   $800          ;beginning of text page 2
2000:        0C00   37 begsprse  equ   $C00          ;byte after text page 2
2000:               38 *
2000:        C000   39 clr80col  equ   $C000         ;disable 80-column store
2000:        C001   40 set80col  equ   $C001         ;enable 80-column store
2000:        C002   41 rdmainram equ   $C002         ;read main ram
2000:        C003   42 rdcardram equ   $C003         ;read aux ram
2000:        C004   43 wrmainram equ   $C004         ;write main ram
2000:        C005   44 wrcardram equ   $C005         ;write aux ram
2000:        C013   45 rdramrd   equ   $C013         ;are we reading aux ram?
2000:        C016   46 rdaltzp   equ   $C016         ;are we reading aux zero page?
2000:        C018   47 rd80col   equ   $C018         ;are we using 80-columns?
2000:        C01A   48 rdtext    equ   $C01A         ;read if text is displayed
2000:        C01C   49 rdpage2   equ   $C01C         ;read if page 2 is displayed
2000:        C050   50 txtclr    equ   $C050         ;switch in graphics
2000:        C051   51 txtset    equ   $C051         ;switch in text
2000:        C054   52 txtpage1  equ   $C054         ;switch in page 1
2000:        C055   53 txtpage2  equ   $C055         ;switch in page 2
2000:        C080   54 ramin     equ   $C080         ;read LC bank 2, write protected
2000:        C081   55 romin     equ   $C081         ;read ROM, 2 reads write enable LC
2000:        C08B   56 lcbank1   equ   $C08B         ;LC bank 1 enable 
2000:               57 *
2000:        E000   58 lc1       equ   $E000         ;bytes to save for LC
2000:        D000   59 lc2       equ   $D000         ;save/restore routine
2000:        D400   60 lc3       equ   $D400
2000:        D800   61 lc4       equ   $D800
2000:               62 *
2000:        FE1F   63 idroutine equ   $FE1F         ;IIgs id routine
2000:               64 *
2000:               65 *  Start by saving the state of the language card banks and
2000:               66 *  by switching in main ROM.
2000:               67 *
2000:08             68 strt      php                 ;save the processor state
2001:78             69           sei                 ;before disabling interrupts
2002:AD 00 E0       70           lda   lc1           ;save four bytes from
2005:8D F1 21       71           sta   save          ;ROM/RAM area for later
2008:AD 00 D0       72           lda   lc2           ;restoring of RAM/ROM
200B:8D F2 21       73           sta   save+1        ;to original condition
200E:AD 00 D4       74           lda   lc3
2011:8D F3 21       75           sta   save+2
2014:AD 00 D8       76           lda   lc4
2017:8D F4 21       77           sta   save+3
201A:AD 81 C0       78           lda   $C081         ;read ROM
201D:AD 81 C0       79           lda   $C081
2020:A9 00          80           lda   #0            ;start by assuming unknown machine
2022:8D E8 21       81           sta   machine
2025:8D E9 21       82           sta   romlevel
2028:               83 *
2028:A5 06          84 IdStart   lda   location      ;save zero page locations
202A:8D F5 21       85           sta   save+4        ;for later restoration
202D:A5 07          86           lda   location+1
202F:8D F6 21       87           sta   save+5
2032:A9 FB          88           lda   #$FB          ;all ID bytes are in page $FB
2034:85 07          89           sta   location+1    ;save in zero page as high byte
2036:A2 00          90           ldx   #0            ;init pointer to start of ID table
2038:BD F7 21       91 loop      lda   IDTable,x     ;get the machine we are testing for
203B:8D E8 21       92           sta   machine       ;and save it
203E:BD F8 21       93           lda   IDTable+1,x   ;get the ROM level we are testing for
2041:8D E9 21       94           sta   romlevel      ;and save it
2044:0D E8 21       95           ora   machine       ;are both zero?
2047:F0 1C   2065   96           beq   matched       ;yes - at end of list - leave
2049:               97 *
2049:E8             98 loop2     inx                 ;bump index to loc/byte pair to test
204A:E8             99           inx
204B:BD F7 21      100           lda   IDTable,x     ;get the byte that should be in ROM
204E:F0 15   2065  101           beq   matched       ;if zero, we're at end of list
2050:85 06         102           sta   location      ;save in zero page
2052:              103 *
2052:A0 00         104           ldy   #0            ;init index for indirect addressing
2054:BD F8 21      105           lda   IDTable+1,x   ;get the byte that should be in ROM
2057:D1 06         106           cmp   (Location),y  ;is it there?
2059:F0 EE   2049  107           beq   loop2         ;yes, so keep on looping
205B:              108 *
205B:E8            109 loop3     inx                 ;we didn't match.Scoot to the end of the
205C:E8            110           inx                 ;line in the ID table so we can start
205D:BD F7 21      111           lda   IDTable,x     ;checking for another machine
2060:D0 F9   205B  112           bne   loop3
2062:E8            113           inx                  ;point to start of next line
2063:D0 D3   2038  114           bne   loop          ;should always be taken
2065:              115 *
2065:        2065  116 matched   equ   * 
2065:              117 *
2065:              118 *  Here we check the 16-bit ID routine at $FE1F.  If it
2065:              119 *  returns with carry clear, we call it again in 16-bit
2065:              120 *  mode to provide more information on the machine.
2065:              121 *
2065:38            122 idIIgs    sec                 ;set the carry bit
2066:20 1F FE      123           jsr   idroutine     ;Apple IIgs ID Routine
2069:90 03   206E  124           bcc   idIIgs2       ;it's a IIgs or equivalent
206B:4C A2 20      125           jmp   exit          ;nope, go check memory
206E:AD E8 21      126 idIIgs2   lda   machine       ;get the value for machine
2071:09 80         127           ora   #$80          ;and set the high bit
2073:8D E8 21      128           sta   machine       ;put it back
2076:18            129           clc                 ;get ready to switch into native mode
2077:FB            130           dfb   xce           ;this is a 65816 XCE instruction
2078:08            131           php                 ;save the processor status
2079:C2 30         132           dfb   $C2,$30       ;REP 30, sets 16-bit registers
207B:20 1F FE      133           jsr   $FE1f         ;call the ID routine again
207E:8D EB 21      134           sta   IIgsA         ;16-bit store!
2081:8E ED 21      135           stx   IIgsX         ;16-bit store!
2084:8C EF 21      136           sty   IIgsY         ;16-bit store!
2087:28            137           plp                 ;restores 8-bit registers
2088:FB            138           dfb   xce           ;switches back to whatever it was before
2089:              139 *
2089:AC EF 21      140           ldy   IIgsY         ;get the ROM vers number (starts at 0)
208C:C0 02         141           cpy   #$02          ;is it ROM 01 or 00?
208E:B0 01   2091  142           bcs   idIIgs3       ;if not, don't increment
2090:C8            143           iny                 ;bump it up for romlevel
2091:8C E9 21      144 idIIgs3   sty   romlevel      ;and put it there
2094:C0 01         145           cpy   #$01          ;is it the first ROM?
2096:D0 0A   20A2  146           bne   IIgsOut       ;no, go on with things
2098:AD F0 21      147           lda   IIgsY+1       ;check the other byte too
209B:D0 05   20A2  148           bne   IIgsOut       ;nope, it's a IIgs successor
209D:A9 7F         149           lda   #$7F          ;fix faulty ROM 00 on the IIgs
209F:8D EB 21      150           sta   IIgsA
20A2:        20A2  151 IIgsOut   equ   *
20A2:              152 *
20A2:              153 ******************************************
20A2:              154 * This part of the code checks for the   *
20A2:              155 * memory configuration of the machine.   *
20A2:              156 * If it's a IIgs, we've already stored   *
20A2:              157 * the total memory from above.  If it's  *
20A2:              158 * a IIc, we know it's 128K; if it's a    *
20A2:              159 * ][+, we know it's at least 48K and     *
20A2:              160 * maybe 64K.  We won't check for less    *
20A2:              161 * than 48K, since that's a really rare   *
20A2:              162 * circumstance.                          *
20A2:              163 ******************************************
20A2:              164 *
20A2:AD E8 21      165 exit      lda   machine       ;get the machine kind
20A5:30 14   20BB  166           bmi   exit128       ;it's a 16-bit machine (has 128K)
20A7:C9 05         167           cmp   #IIc          ;is it a IIc?
20A9:F0 10   20BB  168           beq   exit128       ;yup, it's got 128K
20AB:C9 04         169           cmp   #IIe          ;is it a IIe?
20AD:D0 03   20B2  170           bne   contexit      ;yes, go muck with aux memory
20AF:4C 4E 21      171           jmp   muckaux
20B2:C9 03         172 contexit  cmp   #IIIem        ;is it a /// in emulation?
20B4:D0 6E   2124  173           bne   exitII        ;nope, it's a ][ or ][+
20B6:A9 30         174           lda   #48           ;/// emulation has 48K
20B8:4C BD 20      175           jmp   exita
20BB:A9 80         176 exit128   lda   #128          ;128K 
20BD:8D EA 21      177 exita     sta   memory
20C0:AD 00 E0      178 exit1     lda   lc1           ;time to restore the LC
20C3:CD F1 21      179           cmp   save          ;if all 4 bytes are the same
20C6:D0 18   20E0  180           bne   exit2         ;then LC was never on so
20C8:AD 00 D0      181           lda   lc2           ;do nothing
20CB:CD F2 21      182           cmp   save+1
20CE:D0 10   20E0  183           bne   exit2
20D0:AD 00 D4      184           lda   lc3
20D3:CD F3 21      185           cmp   save+2
20D6:D0 08   20E0  186           bne   exit2
20D8:AD 00 D8      187           lda   lc4
20DB:CD F4 21      188           cmp   save+3
20DE:F0 38   2118  189           beq   exit6
20E0:AD 88 C0      190 exit2     lda   $C088         ;no match! so turn first LC
20E3:AD 00 E0      191           lda   lc1           ;bank on and check
20E6:CD F1 21      192           cmp   save
20E9:F0 06   20F1  193           beq   exit3
20EB:AD 80 C0      194           lda   $C080
20EE:4C 18 21      195           jmp   exit6
20F1:AD 00 D0      196 exit3     lda   lc2
20F4:CD F2 21      197           cmp   save+1        ;if all locations check
20F7:F0 06   20FF  198           beq   exit4         ;then do more more else
20F9:AD 80 C0      199           lda   $C080         ;turn on bank 2
20FC:4C 18 21      200           jmp   exit6
20FF:AD 00 D4      201 exit4     lda   lc3           ;check second byte in bank 1
2102:CD F3 21      202           cmp   save+2
2105:F0 06   210D  203           beq   exit5
2107:AD 80 C0      204           lda   $C080         ;select bank 2
210A:4C 18 21      205           jmp   exit6
210D:AD 00 D8      206 exit5     lda   lc4           ;check third byte in bank 1
2110:CD F4 21      207           cmp   save+3
2113:F0 03   2118  208           beq   exit6
2115:AD 80 C0      209           lda   $C080         ;select bank 2
2118:28            210 exit6     plp                 ;restore interrupt status
2119:AD F5 21      211           lda   save+4        ;put zero page back
211C:85 06         212           sta   location
211E:AD F6 21      213           lda   save+5        ;like we found it
2121:85 07         214           sta   location+1
2123:60            215           rts                 ;and go home.
2124:              216 *
2124:AD 8B C0      217 exitII    lda   lcbank1       ;force in language card
2127:AD 8B C0      218           lda   lcbank1       ;bank 1
212A:AE 00 D0      219           ldx   lc2           ;save the byte there
212D:A9 AA         220           lda   #test1        ;use this as a test byte
212F:8D 00 D0      221           sta   lc2
2132:4D 00 D0      222           eor   lc2           ;if the same, should return zero
2135:D0 12   2149  223           bne   noLC
2137:4E 00 D0      224           lsr   lc2           ;check twice just to be sure
213A:A9 55         225           lda   #test2        ;this is the shifted value
213C:4D 00 D0      226           eor   lc2           ;here's the second check
213F:D0 08   2149  227           bne   noLC
2141:8E 00 D0      228           stx   lc2           ;put it back!
2144:A9 40         229           lda   #64           ;there's 64K here
2146:4C BD 20      230           jmp   exita
2149:A9 30         231 noLC      lda   #48           ;no restore - no LC!
214B:4C BD 20      232           jmp   exita         ;and get out of here
214E:              233 *
214E:AE 1A C0      234 muckaux   ldx   rdtext        ;remember graphics in X
2151:AD 1C C0      235           lda   rdpage2       ;remember current video display
2154:0A            236           asl   A             ;in the carry bit 
2155:A9 88         237           lda   #test3        ;another test character
2157:2C 18 C0      238           bit   rd80col       ;remember video mode in N
215A:8D 01 C0      239           sta   set80col      ;enable 80-column store
215D:08            240           php                 ;save N and C flags
215E:8D 55 C0      241           sta   txtpage2      ;set page two
2161:8D 51 C0      242           sta   txtset        ;set text
2164:AC 00 04      243           ldy   begpage1      ;save first character
2167:8D 00 04      244           sta   begpage1      ;and replace it with test character
216A:AD 00 04      245           lda   begpage1      ;get it back
216D:8C 00 04      246           sty   begpage1      ;and put back what was there
2170:28            247           plp
2171:B0 08   217B  248           bcs   muck2         ;stay in page 2
2173:8D 54 C0      249           sta   txtpage1      ;restore page 1
2176:30 03   217B  250 muck1     bmi   muck2         ;stay in 80-columns
2178:8D 00 C0      251           sta   $c000         ;turn off 80-columns
217B:A8            252 muck2     tay                 ;save returned character
217C:8A            253           txa                 ;get graphics/text setting
217D:30 03   2182  254           bmi   muck3
217F:8D 50 C0      255           sta   txtclr        ;turn graphics back on
2182:C0 88         256 muck3     cpy   #test3        ;finally compare it
2184:D0 2F   21B5  257           bne   nocard        ;no 80-column card!
2186:AD 13 C0      258           lda   rdramrd       ;is aux memory being read?
2189:30 2F   21BA  259           bmi   muck128       ;yup, there's 128K!
218B:AD 16 C0      260           lda   rdaltzp       ;is aux zero page used?
218E:30 2A   21BA  261           bmi   muck128       ;yup!
2190:A0 2A         262           ldy   #done-start
2192:BE BC 21      263 move      ldx   start-1,y     ;swap section of zero page
2195:B9 00 00      264           lda   safe-1,y      ;code needings safe location during
2198:96 00         265           stx   safe-1,y      ;reading of aux mem
219A:99 BC 21      266           sta   start-1,Y
219D:88            267           dey
219E:D0 F2   2192  268           bne   move
21A0:4C 01 00      269           jmp   safe          ;jump to safe ground
21A3:08            270 back      php                 ;save status
21A4:A0 2A         271           ldy   #done-start   ;move zero page back
21A6:B9 BC 21      272 move2     lda   start-1,y
21A9:99 00 00      273           sta   safe-1,y
21AC:88            274           dey
21AD:D0 F7   21A6  275           bne   move2
21AF:68            276           pla
21B0:B0 03   21B5  277           bcs   noaux
21B2:4C BA 21      278 isaux     jmp   muck128       ;there is 128K
21B5:              279 *
21B5:              280 *  You can put your own routine at "noaux" if you wish to
21B5:              281 *  distinguish between 64K without an 80-column card and
21B5:              282 *  64K with an 80-column card.
21B5:              283 *
21B5:        21B5  284 noaux     equ   *
21B5:A9 40         285 nocard    lda   #64           ;only 64K
21B7:4C BD 20      286           jmp   exita
21BA:4C BB 20      287 muck128   jmp   exit128       ;there's 128K
21BD:              288 *
21BD:              289 *  This is the routine run in the safe area not affected
21BD:              290 *  by bank-switching the main and aux RAM.
21BD:              291 *
21BD:A9 EE         292 start     lda   #test4        ;yet another test byte
21BF:8D 05 C0      293           sta   wrcardram     ;write to aux while on main zero page
21C2:8D 03 C0      294           sta   rdcardram     ;read aux ram as well
21C5:8D 00 08      295           sta   begpage2      ;check for sparse memory mapping
21C8:AD 00 0C      296           lda   begsprse      ;if sparse, these will be the same 
21CB:C9 EE         297           cmp   #test4        ;value since they're 1K apart
21CD:D0 0E   21DD  298           bne   auxmem        ;yup, there's 128K!
21CF:0E 00 0C      299           asl   begsprse      ;may have been lucky so we'll
21D2:AD 00 08      300           lda   begpage2      ;change the value and see what happens
21D5:CD 00 0C      301           cmp   begsprse
21D8:D0 03   21DD  302           bne   auxmem
21DA:38            303           sec                 ;oops, no auxiliary memory
21DB:B0 01   21DE  304           bcs   goback
21DD:18            305 auxmem    clc
21DE:8D 04 C0      306 goback    sta   wrmainram     ;write main RAM
21E1:8D 02 C0      307           sta   rdmainram     ;read main RAM
21E4:4C A3 21      308           jmp   back          ;continue with program in main mem
21E7:EA            309 done      nop                 ;end of relocated program marker
21E8:              310 *
21E8:              311 *
21E8:              312 *  The storage locations for the returned machine ID:
21E8:              313 *
21E8:00            314 machine   dfb   $00           ;the type of Apple II
21E9:00            315 romlevel  dfb   $00           ;which revision of the machine
21EA:00            316 memory    dfb   $00           ;how much memory (up to 128K)
21EB:00 00         317 IIgsA     dw    $0000         ;16-bit field
21ED:00 00         318 IIgsX     dw    $0000         ;16-bit field
21EF:00 00         319 IIgsY     dw    $0000         ;16-bit field
21F1:00 00 00 00   320 save      dfb   0,0,0,0,0,0   ;six bytes for saved data
21F7:01 01 B3 38   321 IDTable   dfb   1,1,$B3,$38,$00 ;Apple ][
21FC:02 01 B3 EA   322           dfb   2,1,$B3,$EA,$1E,$AD,$00 ;Apple ][+
2203:03 01 B3 EA   323           dfb   3,1,$B3,$EA,$1E,$8A,$00 ;Apple /// (emulation)
220A:04 01 B3 06   324           dfb   4,1,$B3,$06,$C0,$EA,$00 ;Apple IIe (original)
2211:04 02 B3 06   325           dfb   4,2,$B3,$06,$C0,$E0,$00 ;Apple IIe (enhanced)
2218:05 01 B3 06   326           dfb   5,1,$B3,$06,$C0,$00,$BF,$FF,$00 ;Apple IIc (original)
2221:05 02 B3 06   327           dfb   5,2,$B3,$06,$C0,$00,$BF,$00,$00 ;Apple IIc (3.5 ROM)
222A:05 03 B3 06   328           dfb   5,3,$B3,$06,$C0,$00,$BF,$03,$00 ;Apple IIc (Mem. Exp)
2233:05 04 B3 06   329           dfb   5,4,$B3,$06,$C0,$00,$BF,$04,$00 ;Apple IIc (Rev. Mem. 
Exp.)
223C:05 05 B3 06   330           dfb   5,5,$B3,$06,$C0,$00,$BF,$05,$00 ;Apple IIc Plus
2245:00 00         331           dfb   0,0           ;end of table

 21DD AUXMEM         21A3 BACK           0400 BEGPAGE1       0800 BEGPAGE2      
 0C00 BEGSPRSE      ?C000 CLR80COL       20B2 CONTEXIT       21E7 DONE          
 20E0 EXIT2          20A2 EXIT           20BB EXIT128       ?20C0 EXIT1         
 20F1 EXIT3          20FF EXIT4          210D EXIT5          2118 EXIT6         
 20BD EXITA          2124 EXITII         21DE GOBACK        ?2065 IDIIGS        
 206E IDIIGS2        2091 IDIIGS3        FE1F IDROUTINE     ?2028 IDSTART       
 21F7 IDTABLE          05 IIC              04 IIE            21EB IIGSA         
 20A2 IIGSOUT        21ED IIGSX          21EF IIGSY            03 IIIEM         
?  01 IIPLAIN       ?  02 IIPLUS        ?21B2 ISAUX          E000 LC1           
 D000 LC2            D400 LC3            D800 LC4            C08B LCBANK1       
   06 LOCATION       2038 LOOP           2049 LOOP2          205B LOOP3         
 21E8 MACHINE        2065 MATCHED        21EA MEMORY         21A6 MOVE2         
 2192 MOVE           21BA MUCK128       ?2176 MUCK1          217B MUCK2         
 2182 MUCK3          214E MUCKAUX        21B5 NOAUX          21B5 NOCARD        
 2149 NOLC          ?C080 RAMIN          C018 RD80COL        C016 RDALTZP       
 C003 RDCARDRAM      C002 RDMAINRAM      C01C RDPAGE2        C013 RDRAMRD       
 C01A RDTEXT        ?C081 ROMIN          21E9 ROMLEVEL         01 SAFE          
 21F1 SAVE           C001 SET80COL       21BD START         ?2000 STRT          
   AA TEST1            55 TEST2            88 TEST3            EE TEST4         
 C050 TXTCLR         C054 TXTPAGE1       C055 TXTPAGE2       C051 TXTSET        
 C005 WRCARDRAM      C004 WRMAINRAM        FB XCE           
** SUCCESSFUL ASSEMBLY := NO ERRORS
** ASSEMBLER CREATED ON 15-JAN-84 21:28 
** TOTAL LINES ASSEMBLED   331 
** FREE SPACE PAGE COUNT   81 


Further Reference
o    Apple II Miscellaneous Technical Note #7, Apple II Family Identification

